﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using Medianamik.Core;
using Medianamik.Core.DAL.Repositories;
using Medianamik.Core.Sugar;
using Medianamik.UI.Web.Shop.Data;
using Medianamik.UI.Web.Shop.Data.Repositories;
using Medianamik.UI.Web.MVP.Presentation;
using SubSonic;
using Telerik.Web.UI;

namespace Medianamik.UI.Web.Shop.Presentation.Presenters
{
    public class ProductListPresenter<TProduct, TCategory, TBrand, TProductsInCategories, TView> : ModulePresenterWithGrid<TProduct, TView,
        IProductRepository<TProduct>, INodeTypeRepository, IMedianamikRepository<IMedianamikDTOWithName>>
        where TProduct : class, Data.Interfaces.IProduct
        where TCategory : class, Data.Interfaces.ICategory
        where TBrand : class, Data.Interfaces.IBrand
        where TProductsInCategories : class, Data.Interfaces.IProductsInCategories
        where TView : class, IProducts
    {
        protected readonly ICategoryRepository<TCategory> CategoriesRepository;
        protected readonly IBrandRepository<TBrand> BrandsRepository;
        protected readonly IProductsInCategoriesRepository<TProductsInCategories> ProductsInCategoriesRepository;


        public ProductListPresenter(TView view, IProductRepository<TProduct> productRepository,
            INodeTypeRepository nodeTypeRepository, IMedianamikRepository<IMedianamikDTOWithName> baseTypeRepository,
            ICategoryRepository<TCategory> categoriesRepository, IBrandRepository<TBrand> brandsRepository,
            IProductsInCategoriesRepository<TProductsInCategories> productsInCategoriesRepository)
            : base(view, productRepository, nodeTypeRepository, baseTypeRepository)
        {
            CategoriesRepository = categoriesRepository;
            BrandsRepository = brandsRepository;
            ProductsInCategoriesRepository = productsInCategoriesRepository;
        }

        protected override bool HasMultipleTypes
        {
            get
            {
                return true;
            }
        }

        protected override string DefaultOrderBy
        {
            get
            {
                return "Name";
            }
        }
        
        protected override void Initialize(object sender, EventArgs e)
        {
            base.Initialize(sender, e);

            if (!View.IsPostBack)
            {
                BindBrands(Brand);
                BindCategories(Category);
            }
        }

        protected override IEnumerable<Where> GetAdditionalWheres()
        {
            var wheres = new List<Where>();

            if (SelectedBrand.HasValue)
            {
                wheres.Add(new Where
                {
                    ColumnName = "Brand",
                    Comparison = Comparison.Equals,
                    ParameterValue = SelectedBrand.Value
                });
            }

            if (SelectedCategory.HasValue)
            {//Doit-être un ProductWithCategory pour pouvoir filtrer sur la categorie
                //mais doit retourner un Product puisque nous affichons seulement 1 categorie
                wheres.Add(new Where
                {
                    ColumnName = "Categories",
                    Comparison = Comparison.Equals,
                    ParameterValue = SelectedCategory.Value
                });
            }

            return wheres;
        }

        protected override void SetNameColumn(ITextControl control, TProduct product, bool existInCurrentCulture, int childCount)
        {
            base.SetNameColumn(control, product, existInCurrentCulture, childCount);
            control.Text = "<a href=\"" + BuildUrl(Pages.ModelGroupList, DefaultParamNames.ParentNodeID,
                    product.NodeId) + "\">" + control.Text + "</a>" + FormatChildItemsCount(childCount);
        }

        protected override void SetCustomGridColumns(GridItem item, TProduct element, bool existInCurrentCulture)
        {
            base.SetCustomGridColumns(item, element, existInCurrentCulture);

            SetBrandName(item.FindControl("BrandGridItem") as Literal, element);
            SetCategoryName(item.FindControl("CategoryGridItem") as Literal, element);
        }

        protected virtual void SetBrandName(ITextControl brandNameControl, TProduct element)
        {
            var brandName = "";

            if (element.Brand.HasValue)
            {
                var brand = BrandsRepository.Get(element.Brand.Value);

                if (brand != null)
                {
                    brandName = brand.Name;
                }
            }

            brandNameControl.Text = brandName;
        }

        private IList<TProductsInCategories> _productsInCategoriesList;

        protected IList<TProductsInCategories> ProductsInCategoriesList
        {
            get
            {
                return _productsInCategoriesList ??
                       (_productsInCategoriesList =
                        ProductsInCategoriesRepository.All(CultureInfo.InvariantCulture, ContentState.Draft,
                                                           ActiveStatus.All));
            }

        }

        protected virtual void SetCategoryName(ITextControl categoryNameControl, TProduct element)
        {
            var categoryName = "";

            if (ProductsInCategoriesList.Count > 0)
            {
                var selectedProductsInCategories = ProductsInCategoriesList.DefaultIfEmpty(null)
                    .FirstOrDefault(p => p.Products == element.NodeId);

                if (selectedProductsInCategories != null)
                {
                    var category = Categories.Where(cat => cat.NodeId == 
                        selectedProductsInCategories.Categories).DefaultIfEmpty(null).First();


                    if (category != null)
                    {
                        var level = 0;
                        var categories = Categories;//Pour avoir avec le setter
                        categoryName = CategoriesRepository.GetCategoryNameWithParent(category.ParentNodeId.Value, ref level, ref categories) + category.Name;
                    }

                }
            }

            categoryNameControl.Text = categoryName;
        }

        #region Brands

        private bool _selectedBrandIsRetreived;
        private Guid? GetSelectedBrand()
        {
            if (View.Brands == null)
                return null;

            _selectedBrandIsRetreived = true;
            var selectedBrand = View.Brands.FirstOrDefault(i => i.Selected) ??
                                View.Brands.FirstOrDefault();

            Guid? brand = null;

            if (selectedBrand != null && !String.IsNullOrEmpty(selectedBrand.Value))
                StringHelper.IsGuid(selectedBrand.Value, ref brand);

            return brand;
        }
        private Guid? _selectedBrand;
        private Guid? _brand;
        protected virtual Guid? SelectedBrand
        {
            get
            {
                if (_selectedBrandIsRetreived)
                    return _selectedBrand;

                return _selectedBrand = GetSelectedBrand();
            }
        }

        private IList<TBrand> _brands;
        protected IList<TBrand> Brands
        {
            get { return _brands ?? (_brands = BrandsRepository.All(CurrentUICulture, ContentState.Published, ActiveStatus.All)); }
        }

        protected virtual void BindBrands(Guid? selectedBrand)
        {
            int level = 0;
            IList<TBrand> brands = Brands.Where(c => c.IsActive).ToList();//Pour avoir avec le setter
            var options = Brands.Select(b => new ListItem(
                BrandsRepository.GetBrandNameWithParent(b.ParentNodeId.Value, ref level, ref brands) + b.Name, b.NodeId.ToString())).ToList();
            options = options.OrderBy(item => item.Text).ToList();
            options.Insert(0, new ListItem("Tous", ""));

            if (selectedBrand.HasValue)
            {
                options.First(i => i.Value.Equals(selectedBrand.ToString())).Selected = true;
            }

            View.Brands = options;
        }

        protected virtual Guid? Brand
        {
            get
            {
                return _brand ?? (_brand = GetBrand() ?? DefaultBrand);
            }
        }
        protected virtual Guid? GetBrand()
        {
            return View.IsPostBack ? SelectedBrand : View.QueryString.GetValue<Guid?>("brandid");
        }
        protected virtual Guid? DefaultBrand
        {
            get
            {
                return null;
            }
        }

        #endregion

        #region Category

        private bool _selectedCategoryIsRetreived;
        private Guid? GetSelectedCategory()
        {
            if (View.Categories == null)
                return null;

            _selectedCategoryIsRetreived = true;
            var selectedCategory = View.Categories.FirstOrDefault(i => i.Selected) ??
                                View.Categories.FirstOrDefault();

            Guid? category = null;

            if (selectedCategory != null && !String.IsNullOrEmpty(selectedCategory.Value))
                StringHelper.IsGuid(selectedCategory.Value, ref category);

            return category;
        }
        private Guid? _selectedCategory;
        protected virtual Guid? SelectedCategory
        {
            get
            {
                if (_selectedCategoryIsRetreived)
                    return _selectedCategory;

                return _selectedCategory = GetSelectedCategory();
            }
        }

        private IList<TCategory> _categories;
        protected IList<TCategory> Categories
        {
            get { return _categories ?? (_categories = CategoriesRepository.All(CurrentUICulture,ContentState.Published,ActiveStatus.All)); }
        }

        private void BindCategories(Guid? selectedCategory)
        {
            int level = 0;
            IList<TCategory> categories = Categories.Where(c => c.IsActive).ToList();//Pour avoir avec le setter
            var options = Categories.Select(categorie => new ListItem(
                CategoriesRepository.GetCategoryNameWithParent(categorie.ParentNodeId.Value, ref level, ref categories)
                + categorie.Name, categorie.NodeId.ToString())).ToList();
            options = options.OrderBy(item => item.Text).ToList();
            options.Insert(0, new ListItem("Tous", ""));

            if (selectedCategory.HasValue)
            {
                options.First(i => i.Value.Equals(selectedCategory.ToString())).Selected = true;
            }

            View.Categories = options;
        }

        private Guid? _category;
        protected virtual Guid? Category
        {
            get { return _category ?? (_category = GetCategory()); }
        }

        protected virtual Guid? GetCategory()
        {
            return View.IsPostBack ? SelectedCategory : View.QueryString.GetValue<Guid?>("categoryid");
        }


        #endregion

        protected override string BuildReturnUrl()
        {
            var returnUrl = base.BuildReturnUrl();

            if (Brand.HasValue)
            {
                returnUrl = returnUrl.AddParam("brandid", Brand);
            }

            if (Category.HasValue)
            {
                returnUrl = returnUrl.AddParam("categoryid", Category);
            }

            return returnUrl;
        }
    }

    public class ProductListPresenter : ProductListPresenter<BaseProduct, Category, Brand, ProductsInCategories, IProducts>
    {
        public ProductListPresenter(ShopProducts view, IProductRepository<BaseProduct>
            productRepository, INodeTypeRepository nodeTypeRepository,
            IMedianamikRepository<IMedianamikDTOWithName> baseTypeRepository,
            ICategoryRepository<Category> categoriesRepository,
            IBrandRepository<Brand> brandsRepository,
            IProductsInCategoriesRepository<ProductsInCategories> productsInCategoriesRepository)
            : base(view, productRepository, nodeTypeRepository, baseTypeRepository, categoriesRepository,
            brandsRepository, productsInCategoriesRepository)
        {
        }
    }
}
